package gbench.sandbox.tensor;

import org.junit.jupiter.api.Test;

import gbench.common.fs.XlsFile.DRow;
import gbench.common.fs.XlsFile.DataMatrix;
import gbench.common.tree.LittleTree.IRecord;
import gbench.commonApp.data.DataMatrixApp;
import static gbench.common.tree.LittleTree.IRecord.*;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import static gbench.common.tree.LittleTree.*;

/**
 * tensor 的层状网络计算
 * @author gbench
 *
 */
public class MyTensor extends DataMatrixApp{
    
    @FunctionalInterface
    interface Fxx extends Function<List<IRecord>,IRecord>{}
    
    @FunctionalInterface
    interface F2 extends BiFunction<IRecord,IRecord,IRecord>{}
    @FunctionalInterface
    interface Add extends BiFunction<IRecord,IRecord,IRecord>{}
    @FunctionalInterface
    interface Sub extends BiFunction<IRecord,IRecord,IRecord>{}
    @FunctionalInterface
    interface Mul extends BiFunction<IRecord,IRecord,IRecord>{}
    @FunctionalInterface
    interface Div extends BiFunction<IRecord,IRecord,IRecord>{}
    
    /**
     * 把一个List<IRecord> 转换成 DataMatrix
     * @param rr 以 r 为列向量的 列向量集合
     * @return 浮点数类型的数据矩阵
     */
    public static DataMatrix<Double> rr2mx(final List<IRecord> rr){
        if((rr==null))return null;
       return DataMatrix.of2(rr.stream().map(a->a.values())).corece(Double.class);
    }

    final Add add = (a,b)->a.apply(p->kv2dbl.apply(p)+b.i4(p.key()));
    final Sub sub = (a,b)->a.apply(p->kv2dbl.apply(p)-b.i4(p.key()));
    final Mul mul = (a,b)->a.apply(p->kv2dbl.apply(p)*b.i4(p.key()));
    final Div div = (a,b)->a.apply(p->kv2dbl.apply(p)/b.i4(p.key()));
    
    @Test
    public void foo() {
        println("\nfoo");
        final var x = A2REC(RPTA(10,1.0));// 准备两个原始数据
        final var y = A2REC(RPTA(10,2.0));
        
        final var layer1 = REC("layer1_1", add,"layer1_2", sub, "layer1_3", mul,"layer1_4",div); // 第一层运算节点
        println(layer1.fapply(x, y).toString2(frt(2)));
        final var layer2 = REC(
            "layer2_1",layer1.compose(add, "layer1_1", "layer1_2"),
            "layer2_2",layer1.compose(add, "layer1_3", "layer1_4")
        );// 运算层
        println(layer2.fapply(x, y).toString2());
        
        final var layer3 = REC(
            "layer3_1",layer2.compose(add, "layer2_1","layer2_2")
        );// 运算层
        println(layer3.fapply(x, y).toString2(frt(2)));
    }
    
    
    /**
     * 阶梯的网络计算。
     */
    @Test
    public void bar() {
        println("\nbar");
        final var numbers = "1:3";// 数据长度
        final var dd = cph2(series,numbers,numbers,numbers).collect(dmc);
        println("rawdata:");
        println(dd);
        final var ll = 
            dd.lcols().stream().map(IRecord::L2REC).collect(Collectors.toList()); // 转换成列向量 List<IRecord>
    
        // 网络第一层, 注意计算的时候 一定要按照列进行计算，应为每一列代表一个属性维度。
        final var layer1 = REC(
            "layer1_1",(Fxx)(aa)-> rr2mx(aa).reduceRows(DRow::sum,IRecord::REC), // 转变成行向量集合。
            "layer1_2",(Fxx)(aa)-> rr2mx(aa).reduceRows(e->{
                final var xx = e.toArray();
                return xx[0]+xx[1]-xx[2];
            },IRecord::REC),
            "layer1_3",(Fxx)(aa)->rr2mx(aa).reduceRows(e->{
                final var xx = e.toArray();
                return xx[0]+xx[1]-xx[2];
            },IRecord::REC)
        );//layer1
        println("\nlayer1");
        println(layer1.fapply(ll).toString2());
        
        //网络第二层
        final var layer2 = REC(
            "layer2_1",layer1.compose( (Fxx) xx->rr2mx(xx).reduceRows(DRow::sum,IRecord::REC), // 求layer1_1,layer1_2,layer1_3,的三列和
                "layer1_1", "layer1_2","layer1_3"),
            "layer2_2",layer1.compose( (Fxx) xx->rr2mx(xx).reduceRows(DRow::sum,IRecord::REC),  // 求layer1_1,layer1_2,layer1_3,的三列和
                "layer1_1", "layer1_2","layer1_3"),
            "layer2_3",layer1.compose( (Fxx) xx->rr2mx(xx).reduceRows(DRow::sum,IRecord::REC),  // 求layer1_1,layer1_2,layer1_3,的三列和
                "layer1_1", "layer1_2","layer1_3")
        );// layer2
        println("\nlayer2");
        println(layer2.fapply(ll).toString2());
        
        final var bias_3 = 0d;// 偏移
        final var wts_3 = new Double[] {1d,1d,1d};// 位置权重
        final Function<Double,Double> activation = e->e;// 激活函数
        
        // 网络第三层
        final var layer3 = REC(
            "x",layer2.compose( (Fxx) xx->
            
                // 采用 行的方式的计算 生成一个列向量 IRecord
                // rr2mx(xx).reduceRows(e-> e.reduce(bias_3, (i,t)->wts_3[i]*t, (a,b)->a+b, activation), IRecord::REC), // 使用  activation(bias+sum(wts[i]*tt[i]))
            
                // 采用 矩阵乘法的方式 生成一个列向量 IRecord, 还是矩阵的方式比较简单
                L2REC(rr2mx(xx).mmult(V(wts_3)).add(bias_3).evaluate(activation).lcol(0)),
                
                "layer2_1", "layer2_2","layer2_3") // 对第二层计算的各列结果进行汇集
        );// layer3
        println("\nlayer3");
        println(layer3.fapply(ll).toString2());
        
        //REC(0,1).fxx("0").apply(ll);
    }
    
    
    @Test
    public void qux() {
        final var numbers = "1:3";// 数据长度
        
        final var dd = cph2(series,numbers,numbers,numbers).collect(dmc);
        final var ll = dd.lcols().stream().map(IRecord::L2REC).collect(Collectors.toList()); // 转换成列向量 List<IRecord>
        
        final Function<Double,Double> sigmoid = x->x;
                //x->1/(1+Math.exp(-x));// 激活函数
        final var wts = A( A(10d,8d,9d), A(10d,8d,9d), A(10d,8d,9d) );
        final var bias = A(1d,2d,3d);
        @SuppressWarnings("unchecked")
        final var activations = A(sigmoid,sigmoid,sigmoid);
        BiFunction<Integer,Integer,Fxx> perceptron =(i,j)->  // peceptron 索引
            (rr)-> L2REC(rr2mx(rr).perceptron(bias[i], wts[i], activations[i]).lcol(0)); // Fxx 对象
        
        // 网络第一层, 注意计算的时候 一定要按照列进行计算，应为每一列代表一个属性维度。
        final var layer0 = REC(
            "layer0_1", perceptron.apply(0, 1),
            "layer0_2", perceptron.apply(0, 2),
            "layer0_3", perceptron.apply(0, 3)
        );//layer1
        
        // 网络第二层, 注意计算的时候 一定要按照列进行计算，应为每一列代表一个属性维度。
        final var layer1 = REC(
            "layer1_1",layer0.compose( perceptron.apply(1, 1), "layer0_1", "layer0_2","layer0_3"),
            "layer1_2",layer0.compose( perceptron.apply(1, 2), "layer0_1", "layer0_2","layer0_3"),
            "layer1_3",layer0.compose( perceptron.apply(1, 3), "layer0_1", "layer0_2","layer0_3")
        );//layer1
        
        // 网络第三层, 注意计算的时候 一定要按照列进行计算，应为每一列代表一个属性维度。
        final var layer2 = REC(
            "layer2_1",layer1.compose( perceptron.apply(1, 1), "layer1_1", "layer1_2","layer1_3")
        );//layer1
        
        println(layer2.fapply(ll).toString2(frt(2)));
    }

}
